﻿#include "StdAfx.h"
#include "BlockDesign.h"
#include "PathInfo.h"
#include "CalcDissimilarity.h"
#include "HRTimer.h"
#include "DsegenTypes.h"

namespace dhis
{
	//------------------------------------------------------------------------------
	void _createMacroBlockMap(CBlockChessboard& chessboard,BlockMap& blocks)
	{
		for (int i = 0; i < chessboard.GetBlockCount(); ++i)
		{
			int key = chessboard.GetBlockKey(i);
			CMacroBlock* block = new CMacroBlock;
			block->m_key = key;
			blocks.insert(std::make_pair(key,block));
		}
	}

	//------------------------------------------------------------------------------
	void _deleteMacroBlockMap(BlockMap& blocks)
	{
		BlockMap::iterator itr;
		for (itr = blocks.begin(); itr != blocks.end(); ++itr)
		{
			CMacroBlock* block = itr->second;
			for(ObjectMap::iterator pos = block->m_objects.begin(); pos != block->m_objects.end(); ++pos)
			{
				delete pos->second;
			}
			delete block;
		}
		blocks.clear();
	}

	//------------------------------------------------------------------------------
	bool _insertObject(CBlockChessboard& chessboard,BlockMap& blocks,CRegionObject* obj)
	{
		int row = CALC_ROW(obj->GetId(),chessboard.Width());
		int col = CALC_COL(obj->GetId(),chessboard.Width());
		int key = chessboard.AtBlock(row,col);
		BlockMap::iterator pos = blocks.find(key);
		if (pos == blocks.end()) { return false; }
		return blocks[key]->m_objects.insert(std::make_pair(obj->GetId(),obj)).second;
	}

	//------------------------------------------------------------------------------
	bool _hasOutNeighborObject(CMacroBlock* block,CRegionObject* obj)
	{
		NeighborList& nbrs = obj->GetNeighbors();
		for (NeighborList::iterator itr = nbrs.begin(); itr != nbrs.end(); ++itr)
		{
			if (block->m_objects.end() == block->m_objects.find(itr->m_id)) { return true; }
		}
		return false;
	}

	//------------------------------------------------------------------------------
	void _computeNeighborHeterogeneity(CBlockChessboard& chessboard,BlockMap& blocks,const CImageInfo* pImg,const CParameter* pPara)
	{
		int i;
		if (pPara->m_shapeWeight > 0.0)
		{
#pragma omp parallel for// private(i)
			for (i = 0; i < chessboard.GetBlockCount(); ++i)
			{
				int key = chessboard.GetBlockKey(i);
				CMacroBlock* block = blocks[key];
				for (ObjectMap::iterator pos = block->m_objects.begin(); pos != block->m_objects.end(); ++pos)
				{
					pos->second->CalculatePerimeter();
				}
			}
		}

		static REAL REAL_EPSILON = -(std::numeric_limits<REAL>::epsilon());
		REAL mergedPerim;
#pragma omp parallel for// private(i)
		for (i = 0; i < chessboard.GetBlockCount(); ++i)
		{
			int key = chessboard.GetBlockKey(i);
			CMacroBlock* block = blocks[key];
			ObjectMap& objs = block->m_objects;
			for (ObjectMap::iterator pos = objs.begin(); pos != objs.end(); ++pos)
			{
				if (_hasOutNeighborObject(block,pos->second)) { continue; }
				NeighborList& nbrs = pos->second->GetNeighbors();
				for (NeighborList::iterator itr = nbrs.begin(); itr != nbrs.end(); ++itr)
				{
					if (itr->m_dissimCrit > REAL_EPSILON) { continue; }
					ObjectMap::iterator nbobj = objs.find(itr->m_id);
					assert(nbobj != objs.end());
					itr->m_dissimCrit = calcRegionObjectDissimilarityCriterion(pos->second,nbobj->second,pImg,pPara,mergedPerim);
					itr->m_mergedPerim = mergedPerim;
					NeighborList::iterator findPos = nbobj->second->FindNeighbor(pos->first);
					assert(findPos != nbobj->second->GetNeighbors().end());
					findPos->m_dissimCrit = itr->m_dissimCrit;
					findPos->m_mergedPerim = mergedPerim;
				}
				pos->second->m_utilFlag = true;
				pos->second->SortNeighbor();
				pos->second->CalculateAvgDissim();
			}
		}

		for (i = 0; i < chessboard.GetBlockCount(); ++i)
		{
			int key = chessboard.GetBlockKey(i);
			CMacroBlock* block = blocks[key];
			ObjectMap& objs = block->m_objects;
			for (ObjectMap::iterator pos = objs.begin(); pos != objs.end(); ++pos)
			{
				if (pos->second->m_utilFlag) { continue; }
				NeighborList& nbrs = pos->second->GetNeighbors();
				for (NeighborList::iterator itr = nbrs.begin(); itr != nbrs.end(); ++itr)
				{
					if (itr->m_dissimCrit > REAL_EPSILON) { continue; }
					int nbrkey = chessboard.AtBlock(itr->m_id);
					assert(chessboard.IsBlockKey(nbrkey));
					ObjectMap::iterator nbobj = blocks[nbrkey]->m_objects.find(itr->m_id);
					assert(nbobj != blocks[nbrkey]->m_objects.end());
					itr->m_dissimCrit = calcRegionObjectDissimilarityCriterion(pos->second,nbobj->second,pImg,pPara,mergedPerim);
					itr->m_mergedPerim = mergedPerim;
					NeighborList::iterator findPos = nbobj->second->FindNeighbor(pos->first);
					assert(findPos != nbobj->second->GetNeighbors().end());
					findPos->m_dissimCrit = itr->m_dissimCrit;
					findPos->m_mergedPerim = mergedPerim;
				}
				pos->second->m_utilFlag = true;
				pos->second->SortNeighbor();
				pos->second->CalculateAvgDissim();
			}
		}
	}

	//------------------------------------------------------------------------------
	void _computeStatistics(CMacroBlock* block)
	{
		if (block->m_objects.empty()) { return; }
		block->m_mergedObjs.clear();
		block->m_minDissim = std::numeric_limits<REAL>::max();
		block->m_avgDissim = 0.0;
		REAL prevMinDissim = std::numeric_limits<REAL>::max();
		for(ObjectMap::iterator itr = block->m_objects.begin();  itr != block->m_objects.end(); ++itr)
		{
			if (itr->second->GetNeighbors().begin()->m_dissimCrit < block->m_minDissim) { block->m_minDissim = itr->second->GetNeighbors().begin()->m_dissimCrit; }
			block->m_avgDissim += itr->second->GetAvgDissim();
			if (block->m_minDissim < prevMinDissim)
			{
				prevMinDissim = block->m_minDissim;
				block->m_mergedObjs.clear();
				block->m_mergedObjs.push_back(itr->first);
			} else if (prevMinDissim == block->m_minDissim)
			{
				block->m_mergedObjs.push_back(itr->first);
			}
		}
		block->m_avgDissim = block->m_avgDissim/((REAL)block->m_objects.size());
	}

	//------------------------------------------------------------------------------
	void _computeStatistics(CBlockChessboard& chessboard,BlockMap& blocks,REAL&dAvgDissim,REAL&dMinDissim,int& nObjCount)
	{
		int i;
#pragma omp parallel for// private(i)
		for (i = 0; i < chessboard.GetBlockCount(); ++i)
		{
			int key = chessboard.GetBlockKey(i);
			CMacroBlock* block = blocks[key];
			_computeStatistics(block);
		}

		dAvgDissim = 0.0;
		dMinDissim = std::numeric_limits<REAL>::max();
		nObjCount = 0;
		for (i = 0; i < chessboard.GetBlockCount(); ++i)
		{
			int key = chessboard.GetBlockKey(i);
			CMacroBlock* block = blocks[key];
			if (block->m_objects.empty()) { continue; }
			if(block->m_minDissim < dMinDissim) { dMinDissim = block->m_minDissim; }
			dAvgDissim += (block->m_objects.size()*block->m_avgDissim);
			nObjCount += block->m_objects.size();
		}
		dAvgDissim /= (REAL(nObjCount));
	}

}
